#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据管理模块
负责数据的存储、加载、验证等操作
"""

import os
import json
from typing import List, Dict, Any, Optional, Type, TypeVar
from datetime import datetime
import threading
from concurrent.futures import ThreadPoolExecutor

from models import Student, Course, Grade, User, SystemConfig
from utils import file_manager, logger, validator, performance_monitor
from exceptions import ValidationError, DataNotFoundError, DuplicateDataError, FileOperationError

T = TypeVar('T')


class DataManager:
    """数据管理器"""
    
    def __init__(self, config: SystemConfig):
        self.config = config
        self.students: List[Student] = []
        self.courses: List[Course] = []
        self.grades: List[Grade] = []
        self.users: List[User] = []
        self._lock = threading.RLock()
        self._cache = {}
        self._last_save_time = {}
        
        # 确保目录存在
        file_manager.ensure_directory(os.path.dirname(config.data_file) or '.')
        file_manager.ensure_directory(os.path.dirname(config.courses_file) or '.')
        file_manager.ensure_directory(os.path.dirname(config.grades_file) or '.')
        file_manager.ensure_directory(os.path.dirname(config.users_file) or '.')
        file_manager.ensure_directory(config.backup_dir)
        
        # 加载数据
        self.load_all_data()
    
    def load_all_data(self) -> None:
        """加载所有数据"""
        with self._lock:
            try:
                performance_monitor.start_timer("load_all_data")
                
                # 并行加载数据
                with ThreadPoolExecutor(max_workers=4) as executor:
                    students_future = executor.submit(self._load_data, Student, self.config.data_file)
                    courses_future = executor.submit(self._load_data, Course, self.config.courses_file)
                    grades_future = executor.submit(self._load_data, Grade, self.config.grades_file)
                    users_future = executor.submit(self._load_data, User, self.config.users_file)
                    
                    self.students = students_future.result()
                    self.courses = courses_future.result()
                    self.grades = grades_future.result()
                    self.users = users_future.result()
                
                logger.info(f"数据加载完成: 学生{len(self.students)}人, 课程{len(self.courses)}门, 成绩{len(self.grades)}条, 用户{len(self.users)}个")
                performance_monitor.end_timer("load_all_data")
                
            except Exception as e:
                logger.error(f"加载数据失败: {e}")
                raise FileOperationError(f"加载数据失败: {e}")
    
    def _load_data(self, model_class: Type[T], file_path: str) -> List[T]:
        """加载指定类型的数据"""
        try:
            if not os.path.exists(file_path):
                logger.info(f"文件不存在，创建新文件: {file_path}")
                return []
            
            data = file_manager.safe_read_file(file_path)
            items = []
            
            for line_num, line in enumerate(data, 1):
                try:
                    if hasattr(model_class, 'from_string'):
                        item = model_class.from_string(line)
                    else:
                        # 尝试JSON格式
                        item_data = json.loads(line)
                        item = model_class.from_dict(item_data)
                    
                    items.append(item)
                    
                except Exception as e:
                    logger.warning(f"跳过格式错误的行 {file_path}:{line_num} - {e}")
                    continue
            
            return items
            
        except Exception as e:
            logger.error(f"加载文件失败 {file_path}: {e}")
            return []
    
    def save_all_data(self) -> None:
        """保存所有数据"""
        with self._lock:
            try:
                performance_monitor.start_timer("save_all_data")
                
                # 并行保存数据
                with ThreadPoolExecutor(max_workers=4) as executor:
                    futures = [
                        executor.submit(self._save_data, self.students, self.config.data_file),
                        executor.submit(self._save_data, self.courses, self.config.courses_file),
                        executor.submit(self._save_data, self.grades, self.config.grades_file),
                        executor.submit(self._save_data, self.users, self.config.users_file)
                    ]
                    
                    # 等待所有保存操作完成
                    for future in futures:
                        if not future.result():
                            raise FileOperationError("保存数据失败")
                
                self._last_save_time = {key: datetime.now() for key in ['students', 'courses', 'grades', 'users']}
                logger.info("所有数据保存成功")
                performance_monitor.end_timer("save_all_data")
                
            except Exception as e:
                logger.error(f"保存数据失败: {e}")
                raise FileOperationError(f"保存数据失败: {e}")
    
    def _save_data(self, items: List[T], file_path: str) -> bool:
        """保存指定类型的数据"""
        try:
            data = []
            for item in items:
                if hasattr(item, 'to_string'):
                    data.append(item.to_string())
                else:
                    # JSON格式
                    data.append(json.dumps(item.to_dict(), ensure_ascii=False))
            
            return file_manager.safe_write_file(file_path, data)
            
        except Exception as e:
            logger.error(f"保存文件失败 {file_path}: {e}")
            return False
    
    def add_student(self, student_data: Dict[str, Any]) -> Student:
        """添加学生"""
        with self._lock:
            # 验证数据
            errors = validator.validate_student_data(student_data)
            if errors:
                raise ValidationError(f"学生数据验证失败: {', '.join(errors)}")
            
            # 检查学号是否已存在
            if any(s.student_id == student_data['student_id'] for s in self.students):
                raise DuplicateDataError(f"学号 {student_data['student_id']} 已存在", "student_id")
            
            try:
                student = Student(**student_data)
                self.students.append(student)
                self._invalidate_cache('students')
                logger.info(f"添加学生成功: {student.student_id}")
                return student
            except Exception as e:
                logger.error(f"添加学生失败: {e}")
                raise ValidationError(f"添加学生失败: {e}")
    
    def add_course(self, course_data: Dict[str, Any]) -> Course:
        """添加课程"""
        with self._lock:
            # 验证数据
            errors = validator.validate_course_data(course_data)
            if errors:
                raise ValidationError(f"课程数据验证失败: {', '.join(errors)}")
            
            # 检查课程代码是否已存在
            if any(c.course_id == course_data['course_id'] for c in self.courses):
                raise DuplicateDataError(f"课程代码 {course_data['course_id']} 已存在", "course_id")
            
            try:
                course = Course(**course_data)
                self.courses.append(course)
                self._invalidate_cache('courses')
                logger.info(f"添加课程成功: {course.course_id}")
                return course
            except Exception as e:
                logger.error(f"添加课程失败: {e}")
                raise ValidationError(f"添加课程失败: {e}")
    
    def add_grade(self, grade_data: Dict[str, Any]) -> Grade:
        """添加成绩"""
        with self._lock:
            # 验证数据
            errors = validator.validate_grade_data(grade_data)
            if errors:
                raise ValidationError(f"成绩数据验证失败: {', '.join(errors)}")
            
            # 检查学生是否存在
            if not any(s.student_id == grade_data['student_id'] for s in self.students):
                raise DataNotFoundError(f"学号 {grade_data['student_id']} 不存在", "student")
            
            # 检查课程是否存在
            if not any(c.course_id == grade_data['course_id'] for c in self.courses):
                raise DataNotFoundError(f"课程代码 {grade_data['course_id']} 不存在", "course")
            
            try:
                grade = Grade(**grade_data)
                self.grades.append(grade)
                self._invalidate_cache('grades')
                logger.info(f"添加成绩成功: {grade.student_id}-{grade.course_id}")
                return grade
            except Exception as e:
                logger.error(f"添加成绩失败: {e}")
                raise ValidationError(f"添加成绩失败: {e}")
    
    def add_user(self, user_data: Dict[str, Any]) -> User:
        """添加用户"""
        with self._lock:
            # 检查用户名是否已存在
            if any(u.username == user_data['username'] for u in self.users):
                raise DuplicateDataError(f"用户名 {user_data['username']} 已存在", "username")
            
            try:
                user = User(**user_data)
                self.users.append(user)
                self._invalidate_cache('users')
                logger.info(f"添加用户成功: {user.username}")
                return user
            except Exception as e:
                logger.error(f"添加用户失败: {e}")
                raise ValidationError(f"添加用户失败: {e}")
    
    def get_student(self, student_id: str) -> Optional[Student]:
        """获取学生信息"""
        return next((s for s in self.students if s.student_id == student_id), None)
    
    def get_course(self, course_id: str) -> Optional[Course]:
        """获取课程信息"""
        return next((c for c in self.courses if c.course_id == course_id), None)
    
    def get_user(self, username: str) -> Optional[User]:
        """获取用户信息"""
        return next((u for u in self.users if u.username == username), None)
    
    def search_students(self, **criteria) -> List[Student]:
        """搜索学生"""
        results = self.students
        
        if 'student_id' in criteria and criteria['student_id']:
            results = [s for s in results if criteria['student_id'] in s.student_id]
        
        if 'name' in criteria and criteria['name']:
            results = [s for s in results if criteria['name'] in s.name]
        
        if 'major' in criteria and criteria['major']:
            results = [s for s in results if criteria['major'] in s.major]
        
        if 'class_name' in criteria and criteria['class_name']:
            results = [s for s in results if criteria['class_name'] in s.class_name]
        
        if 'age_min' in criteria and criteria['age_min']:
            results = [s for s in results if s.age >= criteria['age_min']]
        
        if 'age_max' in criteria and criteria['age_max']:
            results = [s for s in results if s.age <= criteria['age_max']]
        
        return results
    
    def search_grades(self, **criteria) -> List[Grade]:
        """搜索成绩"""
        results = self.grades
        
        if 'student_id' in criteria and criteria['student_id']:
            results = [g for g in results if g.student_id == criteria['student_id']]
        
        if 'course_id' in criteria and criteria['course_id']:
            results = [g for g in results if g.course_id == criteria['course_id']]
        
        if 'semester' in criteria and criteria['semester']:
            results = [g for g in results if g.semester == criteria['semester']]
        
        if 'exam_type' in criteria and criteria['exam_type']:
            results = [g for g in results if g.exam_type == criteria['exam_type']]
        
        return results
    
    def update_student(self, student_id: str, update_data: Dict[str, Any]) -> Student:
        """更新学生信息"""
        with self._lock:
            student = self.get_student(student_id)
            if not student:
                raise DataNotFoundError(f"学号 {student_id} 不存在", "student")
            
            # 验证更新数据
            for key, value in update_data.items():
                if hasattr(student, key):
                    setattr(student, key, value)
            
            # 重新验证
            student_dict = student.to_dict()
            errors = validator.validate_student_data(student_dict)
            if errors:
                raise ValidationError(f"更新数据验证失败: {', '.join(errors)}")
            
            self._invalidate_cache('students')
            logger.info(f"更新学生信息成功: {student_id}")
            return student
    
    def delete_student(self, student_id: str) -> bool:
        """删除学生"""
        with self._lock:
            for i, student in enumerate(self.students):
                if student.student_id == student_id:
                    del self.students[i]
                    # 删除相关成绩记录
                    self.grades = [g for g in self.grades if g.student_id != student_id]
                    self._invalidate_cache('students')
                    self._invalidate_cache('grades')
                    logger.info(f"删除学生成功: {student_id}")
                    return True
            
            raise DataNotFoundError(f"学号 {student_id} 不存在", "student")
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取统计信息"""
        with self._lock:
            stats = {
                'students_count': len(self.students),
                'courses_count': len(self.courses),
                'grades_count': len(self.grades),
                'users_count': len(self.users)
            }
            
            if self.students:
                ages = [s.age for s in self.students]
                stats.update({
                    'age_range': (min(ages), max(ages)),
                    'average_age': sum(ages) / len(ages),
                    'majors_count': len(set(s.major for s in self.students)),
                    'classes_count': len(set(s.class_name for s in self.students))
                })
            
            if self.courses:
                stats['total_credits'] = sum(c.credits for c in self.courses)
            
            if self.grades:
                semesters = set(g.semester for g in self.grades)
                exam_types = set(g.exam_type for g in self.grades)
                stats.update({
                    'semesters_count': len(semesters),
                    'exam_types_count': len(exam_types)
                })
            
            return stats
    
    def backup_data(self, backup_name: Optional[str] = None) -> str:
        """备份数据"""
        if not backup_name:
            backup_name = f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        
        backup_dir = os.path.join(self.config.backup_dir, backup_name)
        file_manager.ensure_directory(backup_dir)
        
        try:
            # 保存所有数据到备份目录
            backup_config = SystemConfig(
                data_file=os.path.join(backup_dir, "students.txt"),
                courses_file=os.path.join(backup_dir, "courses.txt"),
                grades_file=os.path.join(backup_dir, "grades.txt"),
                users_file=os.path.join(backup_dir, "users.txt")
            )
            
            temp_manager = DataManager(backup_config)
            temp_manager.students = self.students.copy()
            temp_manager.courses = self.courses.copy()
            temp_manager.grades = self.grades.copy()
            temp_manager.users = self.users.copy()
            temp_manager.save_all_data()
            
            logger.info(f"数据备份成功: {backup_dir}")
            return backup_dir
            
        except Exception as e:
            logger.error(f"数据备份失败: {e}")
            raise FileOperationError(f"数据备份失败: {e}")
    
    def _invalidate_cache(self, data_type: str) -> None:
        """清除缓存"""
        if data_type in self._cache:
            del self._cache[data_type]
    
    def auto_save_if_needed(self) -> None:
        """检查是否需要自动保存"""
        now = datetime.now()
        for data_type, last_save in self._last_save_time.items():
            if (now - last_save).total_seconds() > self.config.auto_save_interval:
                logger.info(f"自动保存 {data_type} 数据")
                self.save_all_data()
                break
